package modelPackage;

import java.awt.Image;
import java.awt.Point;
import java.util.ArrayList;
import modelPackage.Animation;

public abstract class Sprite  {

	//These variables are set by the key listeners, and they are used to check whether to update positions.
	protected boolean posX = false;
	protected boolean negX = false;
	protected boolean jump = false;
	protected boolean posZ = false;
	protected boolean negZ = false;
	
	
	
	//A sprite should know it's own height, width and depth.
	private int height; 
	private int width; 
	private int depth;
		
	//And it should also know it's position on the map in pixels.
	protected Point3D position;
	
	//The active method for Collectible means its on the map. 
	//For characters, active method means its still alive
	private boolean active = true;
	private boolean dead = false;
	
	//A sprite should also know all of the animations that it can possibly ever have.
	private ArrayList<Animation> animations;
	
	//Velocity in the Y direction will change because of gravity.
	protected double vy = 0;
	
	//This lets the update positions thing know whether or not the sprite is flying.
	private boolean flying = false;
	
	//This lets the model know what kind of collisions to use.
	//2D will be a 2, 3D will be a 3.
	private int mode;
	
	
	
	
	/*
	 * Here begins the methods for Sprites.  We start with a constructor.
	 * */
	
	
	/**
	 * This constructor should never be called except in the case of: super();
	 */
	public Sprite(int height, int width, Point position) {
		this.height = height;
		this.width = width;
		this.depth = 50;
		this.position = new Point3D(position, 0);		
		animations = new ArrayList<Animation>();
		this.mode = 2;
	}
	
	
	
	
	/*
	 * This next section deals with movement.  It will handle updating each sprite in every direction.
	 * These functions should only ever be called from a subclass, that's where the vx/vz/vy comes from.
	 * */

	public int updateXPosition(long elapsedTime, double vx){
		if (posX == true) {		
			return (int) (position.x + vx*elapsedTime);
		}
		if (negX == true) {
			return (int) (position.x - vx*elapsedTime);
		}
		else {
			return (int) position.x;
		}
	}
	public int updateZPosition(long elapsedTime, double vz) {
		if (posZ == true) {		
			return (int) (position.z + vz*elapsedTime);
		}
		if (negZ == true) {
			return (int) (position.z - vz*elapsedTime);
		}
		else {
			return (int) position.z;
		}
	}
	
	// Because of gravity, this one works a little bit different.
	public int updateYPosition(long elapsedTime) {
		return (int) (position.y + vy*elapsedTime);
	}

	
	
	/*
	 * Now I need functions to set the position.
	 * */
	public void setPosition(int x, int y, int z) {
		this.get3DPosition().setPosition(x, y, z);
	}
	public void setPosition(Point3D p) {
		this.get3DPosition().setPosition(p);
	}
	public void setPosition(Point p) {
		this.get3DPosition().x = p.x;
		this.get3DPosition().y = p.y;
		//No change in the Z.
	}
	public void setXPosition(int x) {
		this.get3DPosition().x = (double) x;
	}
	public void setYPosition(int y) {
		this.get3DPosition().y = (double) y;
	}
	public void setZPosition(int z) {
		this.get3DPosition().z = (double) z;
	}
	

	
	/*
	 * These are the functions which are used by the key listeners to tell the update which direction to go.
	 * */
	public void setPosXVelocity(boolean moveRight){
		this.posX = moveRight;
	}	
	public void setNegXVelocity(boolean moveLeft){
		this.negX = moveLeft;
	}
	public void setPosZVelocity(boolean moveRight){
		this.posZ = moveRight;
	}	
	public void setNegZVelocity(boolean moveLeft){
		this.negZ = moveLeft;
	}
	public void setYVelocity(double vy){
		this.vy = vy;
	}
		
	
	
	/*
	 * These functions are necessary because of the way it's written. Which is kind of poorly.
	 */
	public abstract double getXVelocity();
	public abstract double getZVelocity();
	
	
	/**
	 * Abstract method to update the sprite when a collision is detected with another sprite
	 */
	public abstract void collide();

	
	
	/*
	 * I would have liked to call these something else now that 3D is involved, however,
	 * for the sake of not breaking anything in the 2D manager, I'll let them be as is. 
	 * */
	
	/**
	 * Returns the current position of the Sprite 
	 */
	public Point getPosition() {
		return new Point((int) this.position.x, (int) this.position.y);
	}
	
	public Point3D get3DPosition() {
		return this.position;
	}

	
	
	/*
	 * And a few get functions for sprite dimensions.
	 * */
	public int getHeight() {
		return height;
	}
	public int getWidth() {
		return width;
	}
	public int getDepth() {
		return depth;
	}
	
	
	
	/*
	 * Some getters for the velocity booleans.
	 * */
	public double getYVelocity() {
		return vy;
	}	
	public boolean getPosXVelocity() {
		return posX;
	}	
	public boolean getNegXVelocity() {
		return negX;
	}
	public boolean getPosZVelocity() {
		return posZ;
	}	
	public boolean getNegZVelocity() {
		return negZ;
	}
	
	
	/*
	 * Some setters and getters for the flying boolean.
	 * */
	public boolean getFlying() {
		return flying;
	}
	public void setFlying(boolean f) {
		this.flying = f;
	}
	
	
	
	/*
	 * This next set of functions allows some control over the animations.
	 * */
	
	/**
	 * This sets the active animation.  This allows many animations to be stored, but only one to be used at a time.
	 */
	public void setAnimation(String animation) {
		if (animations.size() != 0) {
			for (int i = 0; i < animations.size(); i++) {
				if (animations.get(i).getName() == animation) {
					animations.get(i).setActive(true);
				} 
				else {
					animations.get(i).setActive(false);
				}
			}
		}
	}
	
	/**
	 * Used to populate the list of possible animations.
	 * @param animation The animation to add.
	 */
	public void addAnimation(Animation animation) {
		this.animations.add(animation);
	}
	
	/**
	 * Used to retrieve which image should be drawn to the screen. Gets the image from the active animation.
	 * @return The current image to be drawn to the screen.
	 */
	public Image getImage()	{
		return getAnimation().getImage();
	}
	
	
	/**
	 * Used by the getImage function to get the active animation.
	 * @return The current active animation.
	 */
	private Animation getAnimation() {
		if (animations.size() != 0) {
			for (int i = 0; i < animations.size(); i++) {
				if (animations.get(i).getActive() == true) {
					return animations.get(i);
				} 
				else {
					//Do nothing.
				}
			}
		} 
		return null;
	}

	public abstract void initializeAnimations();
	
	public void updateAnimation(Long elapsedTime) {
		this.getAnimation().update(elapsedTime);
	}
	
	/*
	 * These next functions are used to make enemies die.
	 * */
	
	public void setActive(boolean isActive){
		this.active = isActive;
	}
	
	public boolean getActive(){
		return active;
	}
	
	public void setDead(boolean isDead) {
		this.dead = isDead;
	}
	
	public boolean getDead() {
		return this.dead;
	}
	
	
	/*
	 * Used to make the model do the right kind of collisions.
	 * */
	public void setMode(int mode) {
		this.mode = mode;
	}
	
	public int getMode() {
		return this.mode;
	}
	
	
}